docs(agents): os-dev.md :286–:287 re-keyed to the register's two tiers; H43/H48 stand down on Tier S - #19802
Conversation
…ing tiers The two lines still spelled the superseded split (fact layer = the pm-dispatch references directory only, everything else rules layer). The register and AGENTS.md Prime Directive #14 now tier the landing: a PR whose governed paths all lie under `.claude/**` is Tier S and lands on the owning seat's contract review record; every other governed surface is Tier H and waits for the maintainer's word. The maintainer brief draft is owed on every governed PR, on both tiers — the second line stops exempting a "fact layer". Net 0 lines; both lines within the 120-byte cap. Claude-Session: https://claude.ai/code/session_01Wnstp2kTth7sGXfr8fXypc Co-authored-by: Claude <noreply@anthropic.com>
…e register, never a second list Both rows prescribed the maintainer's word on every governed PR: H43 a review request to GOVERNED_APPROVERS (or the assign fallback), H48 the needs-user-decision inbox label plus the final brief comment behind a seat's ACCEPT verdict. The landing is tiered now — a PR whose governed paths all lie under `.claude/**` lands on the owning seat's contract review record with no approval and no maintainer click — so on such a PR each remedy asked for exactly the click the tiering removed, and H43's "the queue guard still refuses an unapproved governed enqueue" rationale was false there. The register the sweep already loads lazily now also carries `landingTierOf` and `GOVERNED_TIER_S`; one helper, `governedLandsOnRecord`, asks it on the slice the matcher already returned (ALL-quantified: one Tier H row keeps a mixed diff in both rows; an empty, tier-less or unloaded answer stands nothing down). H43's probe policy reads the same slice as its row, so a Tier S PR buys no review page and takes no slot under the oldest-first cap. Row sentences and both summary clauses name Tier H as the population. Self-test: 15 cases driven by the real register's own slices; 4897 -> 4912, no new battery, no new row. Claude-Session: https://claude.ai/code/session_01Wnstp2kTth7sGXfr8fXypc Co-authored-by: Claude <noreply@anthropic.com>
…-rekey Pre-PR merge; the incoming commit touches neither os-dev.md nor check-half-states.mjs. Claude-Session: https://claude.ai/code/session_01Wnstp2kTth7sGXfr8fXypc Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: ① Derived judgmentsRendered in-seat by the skills seat 1 session at the served tier the maintainer named (ruling record 5771798588; 「契约复审继续用 fable 不变」 5788482806 of this date). The contract is the card #19146 as graded by the lane (class (b), Path: none) with os-steve's Release 5773572115 / 5773579070 naming the remainder:
② Semver levelNone — no release. ③ Boundary flags
Implemented-by: VERDICT: PASS |
…H / Tier S (objectstack-ai#19820) Fixes objectstack-ai#19146 Clause-②: no ## What this PR does Re-keys the four remaining PM-charter lines that still spelled the retired 规则层 / 事实层 two-layer split to the register's own two tiers — **Tier S** (every governed path under `.claude/**`; the owning seat lands it through the queue on a `## Contract review` PASS record) and **Tier H** (every other governed surface; draft until an authorized APPROVED review) — exactly as `.claude/agents/os-dev.md` :286–:287 already spells them since PR objectstack-ai#19802 (`4a12ac0c4`). The wording model is ruling B on objectstack-ai#19061 (comment 5770886272) as `os-dev.md` carries it; this delivery completes the remainder named by the seat's Release 5790724014 on the card. Vocabulary only: each rule's meaning is unchanged, and no line is added or removed in any file. ### The four lines, before and after (bytes against the 120-byte cap) 1. `.claude/skills/pm-dispatch/SKILL.md` :187 (118 B → 116 B) - before 「- 路径面命中规则层 ⇒ ACCEPT 换终局四件套,混合 diff ⛔ 不按比例判;要拆让 dev 单独开 PR。」 - after 「- 路径面命中 Tier H ⇒ ACCEPT 换终局四件套,混合 diff ⛔ 不按比例判;要拆让 dev 单独开 PR。」 - 规则层 here meant a Tier H path: one Tier H path hit switches ACCEPT to the terminal four-set; the mixed-diff clause is untouched. 2. `.claude/skills/pm-dispatch/SKILL.md` :188 (111 B → 110 B) - before 「- 改动 >5000 行(含生成物)同换终局四件套,⛔ 无事实层例外;读数 = PR additions+deletions。」 - after 「- 改动 >5000 行(含生成物)同换终局四件套,⛔ 无 Tier S 例外;读数 = PR additions+deletions。」 - 事实层 here meant Tier S: the size limb has NO Tier S exception — the rule the queue guard applied to PR objectstack-ai#19803 today; only the word moved. 3. `.claude/skills/pm-dispatch/references/landing-operations.md` :58 (117 B → 115 B) - before 「- 转 ready/入队前跑 `check-governed-merges.mjs --pr N`:受管面照两层;>5000 行(含生成物)照规则层。」 - after 「- 转 ready/入队前跑 `check-governed-merges.mjs --pr N`:受管面照两层;>5000 行(含生成物)照 Tier H。」 - 规则层 here meant the Tier H terminal (四件套, a human merge); :59–:60 beside it already spell Tier S / Tier H. 4. `.claude/skills/pm-dispatch/references/lanes/skills.md` :17 (113 B → 111 B) - before 「- 规则层受管 PR 恒 draft 等维护者的字;flip 或 arm 前跑 `check-governed-merges --test` 带路径。」 - after 「- Tier H 受管 PR 恒 draft 等维护者的字;flip 或 arm 前跑 `check-governed-merges --test` 带路径。」 Line counts unchanged: SKILL.md 319 / 319 (headroom 0), landing-operations.md 101 / 101 (headroom 0), lanes/skills.md 32 / 33 (headroom 1). Widest edited line 116 B. The edit was applied by an anchor-counted replace (each old line asserted to occur exactly once, each new line read back exactly once, line count asserted equal), not by a regex tool's exit code. ### Premise and concurrency, measured - H1 on `origin/main` `6eaa0f4a8`: `git grep -n -E '规则层|事实层' origin/main -- .claude/skills/pm-dispatch/ .claude/agents/` → exactly the four lines above, nothing else. After the edit: 0 hits on that surface, and the card's own acceptance grep (`grep -rn 事实层 .claude AGENTS.md`) → 0 hits. - The only remaining whole-tree hits are `scripts/pm/check-governed-queue-guard.mjs` :236 / :240, inside a verbatim maintainer-ruling quotation that the file's own header marks as reproduced rather than paraphrased — not a spelling of the retired split, not on this surface, not edited. - H3 at PR-open time: all 17 open PRs' file lists read over REST (PR objectstack-ai#17076's 773 / 773 files paged to a short page) — none touches any of the three files. - Register: `node scripts/pm/check-governed-merges.mjs --test` on the three paths → `GOVERNED — Tier S(席内达档复核落地)`, exit 3. ### Landing — Tier S `.claude/**` is on the register as Tier S. This PR is opened as a DRAFT and stays draft: the owning seat renders the `## Contract review` record for the current head and lands it through the queue once every check is green. No seat approves it and no maintainer click is waited for. objectstack-ai#19803 is not addressed here; it is cited only as the guard's reading of the size limb. ### Gates at head `1282c7efc` `1282c7efc` = `8f7e56a6c` (the four-line commit) merged with `origin/main` `0e06f3b5d`; the three-dot delta vs `origin/main` is exactly the three files, +4 −4. `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` (no paths; changeset from the merge base) derived 20 families, the same list at `8f7e56a6c` and at `1282c7efc`. Every command was run on `1282c7efc` with its exit captured before any pipe (stdout+stderr redirected to a file, the exit read on the next statement): 19 × exit 0, 1 × exit 3. Reconciliation `--ran` → `20 derived famil(ies) accounted for — 19 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3)`, exit 0. - `check:pm-skill-ratchet` exit 0 — `.claude/skills/pm-dispatch/SKILL.md is 319 lines (ceiling 319; headroom 0)`, `widest table row is 342 bytes (pin 342; headroom 0)`; `references/landing-operations.md is 101 lines (ceiling 101; headroom 0)`; `references/lanes/skills.md is 32 lines (ceiling 33; headroom 1)`. Same readings before the edit at `6eaa0f4a8`. - `check:skill-frame-sync` exit 0 — the one declared copy of the decision frame is internally coherent, 4 axes; the frame block (SKILL.md :241–:261) is untouched. - `check:pm-skill-id-lint` exit 0 (34 files clean) · `check:nul-bytes` exit 0 · `check:doc-authoring` exit 0 (16192 customer-facing strings clean) · `check:pm-governed-prose` exit 0 (2 surfaces name all 6 registered governed surfaces) · `check:pm-expected-skips` exit 0 (99 cases). - `check:pm-governed-merges` exit 0 (441 assertions) · `check-governed-queue-guard.mjs --self-test` exit 0 (279 cases) · `check-harness-current.mjs --self-test` exit 0 (26 cases) · `check-closing-keyword-parity.mjs` self-test 40 assertions, sweep OK (3 parsers agree on all 9 keywords) · `check:watch-hint-literal` exit 0 (71 declarations) · `check:gitlink-declared` exit 0 (36 assertions). - `check-comment-mask-corpus.mjs` exit 0 (7020 files, 0 disagree) · `check:agent-test-spelling` exit 0 (0 violations) · `check:cross-package-test-inputs` exit 0 (255 self-test cases; 29 packages, all declared) · `check:driver-memory-census` exit 0 · `check:refd-timer-probe` exit 0 (11 cases). - NOT MEASURED: `pnpm --filter @objectstack/lint run check:doc-formula-expressions` exit 3 = `PREREQUISITE NOT MET — the workspace package @objectstack/formula is not built`. This diff touches no package, so no ① build closure is owed, and that family's population (docs formula expressions) is disjoint from the three paths. No package is touched: no package build or test, no verify-lock slot taken. `skills/**` (the published catalog) is untouched, so no whole-file / whole-package token readings are owed. No changeset: `.claude/**` publishes nothing from any package's `files[]` ⇒ `skip-changeset`. ## 维护者速读(草稿) - **改了什么**:PM 派发技能(`pm-dispatch`)里还剩四行沿用已废弃的「规则层 / 事实层」两层叫法(SKILL.md 两行、落地操作一行、skills 车道一行),本 PR 把它们改成登记表现行的 Tier H / Tier S 词汇,与 `os-dev.md` 已落地的写法一致。只换词,不换规则:每条规则的意思、每个文件的行数都不变。 - **为什么改**:每个席位每轮都读这几行;词汇与登记表、AGENTS.md 第 14 条不一致,席位会按一个已不存在的分层去判断 PR 该怎么落地——例如把「无事实层例外」读成一条无人能对上号的例外。 - **风险与代价(含回滚)**:零功能改动,不触任何发布包;派生门禁 20 族 19 绿、1 未测(formula 包未构建,与本 diff 无关,CI 会跑)。回滚即还原四行,无迁移、无数据影响。 - **席位意见**:(待席位填写) - **你要做的**:无需操作;`.claude/**` 属 Tier S,由席位在契约复核 PASS 记录上入队落地。 ## Acceptance notes - noted, not filed: `scripts/pm/check-governed-queue-guard.mjs` :236 / :240 still carry 事实层 / 规则层 inside the verbatim maintainer-ruling quotation the file reproduces on purpose — immutable, and not a live instance of the retired split. --- _Generated by [Claude Code](https://claude.ai/code/session_01Wnstp2kTth7sGXfr8fXypc)_ Co-authored-by: Claude <noreply@anthropic.com>
Part of #19146
Clause-②: no
维护者速读(草稿)
改了什么:两处。①
.claude/agents/os-dev.md:286–:287 两行(净 0 行,402/402)从已废止的「事实层 =references/目录、余为规则层」改写成登记表现行的两层:受管路径全在.claude/**者 Tier S,达档复核 PASS 在案即由席位入队落地;余皆 Tier H 等人批;## 维护者速读(草稿)两层同欠(第二行不再豁免「事实层」)。②scripts/pm/check-half-states.mjs的 H43(受管 PR 未向授权账户请审)与 H48(ACCEPT 后缺needs-user-decision标签或速读评论)在 Tier S PR 上停手:懒加载的登记表多带出landingTierOf与GOVERNED_TIER_S,一个governedLandsOnRecord助手在两行已匹配的切片上问登记表,S 则不判;混合 diff 有一条 Tier H 命中仍判;登记表没加载则一律不停手。自测新增 15 例(4897 → 4912),用真实登记表自己的切片驱动;⛔ 不新增巡查行、不新增电池。为什么改:这两行是每个 dev 子代理都读的自我定义,写的是 2026-09-18 分层裁决之前的边界;H43/H48 在 Tier S PR 上开的处方(请授权账户审、挂维护者收件箱标签)正是裁决取消的那一下点击,且 H43 的理由句「队列守卫仍拒收未批准的受管入队」对 Tier S 为假(PR #19351 零批准、凭复核记录经队列落地)。
风险与代价(含回滚):文字面零行为变化。H43/H48 在 Tier S PR 上不再报告——这是裁决的意图,不是丢失:Tier S 的落地由队列守卫按
## Contract review记录把关。Tier H 与混合 diff 的行为字节不变;登记表缺席时仍照旧报 NOT MEASURED。消融证明:把 tier 读取删掉,5/4912 例转红,还原后 blob == HEAD。回滚 = revert 两个提交(各一类,可单独回退)。席位意见:(席位填写)
你要做的:本 PR 为 Tier S(
.claude/**),由归属席位在## Contract reviewPASS 记录 + 全绿后经队列落地,⛔ 不需要您点击。卡上余项一条(SKILL.md:618,见下方 On the card),由 skills 席位自处置。Summary
The two
os-dev.mdlines still spelled the pre-tiering split (fact layer = thereferences/directory, everything else rules layer). They are re-keyed to the register's rule asAGENTS.mdPrime Directive #14 states it: a PR whose governed paths ALL lie under.claude/**is Tier S and lands on the owning seat's## Contract reviewrecord; every other governed surface is Tier H and waits for the maintainer's word. The maintainer-brief draft is owed on both tiers (SKILL.md :623 has no tier split; the seat fills 席位意见 on both), so the second line simply stops exempting a "fact layer".The card also names
check-half-states.mjsH48 and H43. Measured on the base: both rows still exist and are tier-blind (governedTierFor/landingTierOf: 0 hits in the file; H43 readsGOVERNED_APPROVERSonly). On a Tier S PR each remedy asks for the click the tiering removed, so both stand down there. The tier is the register's own answer on the slice the matcher already returned — no second list of surfaces lives in the patrol.Per-site before → after
.claude/agents/os-dev.md(402 → 402 lines, net 0; both lines within the 120-byte cap).claude/skills/pm-dispatch/references/者为事实层,席位复审即记录;余为规则层。」.claude/**者 Tier S,达档复核 PASS 在案即由席位入队落地;余皆 Tier H 等人批。」## 维护者速读(草稿)节,中文、业务角度,席位意见留空;事实层不欠。」## 维护者速读(草稿)节,中文、业务角度,席位意见留空;两层同欠。」The rule they now mirror:
AGENTS.md:272–:280 (Tier H =docs/adr/**,docs/NORTH-STAR.md,skills/**,AGENTS.md,CLAUDE.md, an authorized APPROVED review; Tier S = all of.claude/**, a## Contract reviewrecord for the current head withServed-tier: CONTRACT_REVIEW_TIERand a PASS verdict, the owning seat lands it through the queue) and SKILL.md :623 (「草稿归 dev:受管面 PR 正文带## 维护者速读(草稿)」, no tier split).grep -n 事实层 .claude/agents/os-dev.mdon the head: 0 hits.scripts/pm/check-half-states.mjs(H43 / H48)loadGovernedRegistergovernedPathsIn+GOVERNED_APPROVERSlandingTierOf+GOVERNED_TIER_S(tierOf,recordTier); a register missing any of the four is "did not export what this row reads", as beforegovernedLandsOnRecord(governed, register)trueonly when the register is available ANDlandingTierOf(slice) === GOVERNED_TIER_S;falsefor a Tier H or mixed slice, an empty or tier-less slice, or an unloaded registerh43NeedsReviewProbe(pr, governedCount, approvers)h43GovernedReviewRequestGapnullon a Tier S slice; the sentence reads "open and GOVERNED on Tier H" and "refuses an unapproved Tier H enqueue"h48SpeaksAbouth48GovernedVerdictWithoutBriefisnullthere and the sweep buys no PR comment thread for ith43NeedsReviewProbe(pr, governedByPr.get(n)?.length ?? 0, …)h43NeedsReviewProbe(pr, governedByPr.get(n) ?? [], …)Self-test: 15 new
t()cases (no new battery, no floor moved): the Tier S and mixed slices come fromGOVERNED_REGISTER.matcher(...)on the real register, so the tier answer is the register's; the hand-builtGOV43/GOV48fixtures carry notierand read as H (fail closed), which the comments now say. Register pin extended:tierOf(matcher(['.claude/agents/os-dev.md'])) === recordTier. Case counts: 4897 on the base → 4912.Why stand down rather than re-aim: the card offered both. Re-aiming H43 at "a Tier S PR without a review of record" would be a new patrol row in disguise (新增门禁默认否), and the queue guard already refuses a Tier S enqueue without the record — nothing ships through that gap. Standing down is the minimal, mechanical repair the card's item 3 spells for H48 and it is the same change class for H43, so the two ride together.
Measurement the change rests on (reads taken 2026-09-23T05:24Z–05:36Z against
origin/main=2cf9db7c4; each item names its own clock).claude/agents/os-dev.md:286–:287 read byte-identical to the card's quotation (premise valid).check-half-states.mjs@2cf9db7c4: H48 row at :10888–:11100, H43 row at :9607–:9850;governedTierFor/landingTierOf/recordTier: 0 hits in the 34,841-line file;GOVERNED_APPROVERSis H43's only firing control. PR pm: retire the needs:contract-review label from the tree (ruling B step ③) #19737 retired H31/H35/H51/H53/H61 and left both rows in place.Unlock-action:value — a label-transition exit on a named card (#19255) #19351, all.claude/**+scripts/pm/): 0 reviews, no review request, no**ACCEPT**on the thread, noneeds-user-decision, no## 维护者速读comment — one## Contract reviewPASS record, landed through the queue. H43's shape fires on exactly that PR; it fired on fix(pm): repair two carriers still spelling the superseded references-tier boundary #19379 too (card comment 5750573385).check-governed-merges.mjs --test→GOVERNED — Tier S(席内达档复核落地), exit 3 (= EXIT_TEST_GOVERNED);scripts/pm/check-half-states.mjsis not on the register.Reverse verification (ablation), run 2026-09-23T05:37Z at head
bd5bbd2bb(the file is byte-identical atc9617adde)node scripts/ablation-replace.mjs --file scripts/pm/check-half-states.mjs --anchor ' return register.tierOf(list) === register.recordTier;' --replacement ' return false; // ABLATION: tier reading removed' -- node scripts/pm/check-half-states.mjs --self-testd2d9ba38ac63→52c8e2e35f0d✗ check-half-states self-test: 5 of 4912 case(s) failed(the H43 Tier S clean case, the H43 Tier S probe case, the register-answer join case, the H48 Tier S population case, the H48 Tier S no-finding case); the mixed-slice controls stayed greend2d9ba38ac63== blob at HEAD;git diff HEADempty; re-read on the absolute path: anchor 1, marker 0dist/is involved (the patrol runs from source), so no build legGates on the final head
c9617adde(run 2026-09-23T06:02Z–06:19Z; exit codes captured before any pipe; verdict lines from the gate logs)Derived with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandson this head (47 commands, list identical to the derivation atbd5bbd2bb);--ranreconciliation:✓ dispatch-gates --ran: 47 derived famil(ies) accounted for — 46 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3)..node scripts/check-ci-filter-parity.mjscoreorcrosspkg, everycrosspkgentry still covers one, and thetestjnode scripts/check-closing-keyword-parity.mjsnode scripts/check-closing-keyword-parity.mjs --self-testnode scripts/check-comment-mask-corpus.mjsnode scripts/check-declaration-mirrors.mjsnode scripts/check-declaration-mirrors.mjs --self-testnode scripts/check-scripts-symbol-anchors.mjsnode scripts/check-scripts-symbol-anchors.mjs --self-testnode scripts/check-self-test-wired.mjs--self-testhas that self-test run by CI.node scripts/check-self-test-wired.mjs --self-testnode scripts/check-self-test-workflow-commands.mjs--self-test(0 of them package-local gate(s) CI names by path, present because this population is the one checnode scripts/check-self-test-workflow-commands.mjs --self-testnode scripts/check-whole-set-label-write.mjsrun()refuse rather than pass if it ever stops finding them.node scripts/check-whole-set-label-write.mjs --self-testnode scripts/pm/bare-root-worklist.mjs --self-testnode scripts/pm/board-snapshot.mjs --self-testnode scripts/pm/check-governed-queue-guard.mjs --self-testnode scripts/pm/check-harness-current.mjs --self-testnode scripts/pm/sweep-closed-cards.mjs --self-testpnpm --filter @objectstack/lint run check:doc-formula-expressionspnpm check:agent-model-declaredpnpm check:agent-test-spelling--token(s) · 1810 launcher-rooted run(s) · 13 separator(s) JUDGED · 6 vitest-bapnpm check:bash32-floorpnpm check:cli-command-idspnpm check:closing-target-claimpnpm check:commit-card-trailerspnpm check:cross-package-test-inputspnpm check:doc-authoringpnpm check:driver-memory-censuspnpm check:entry-guardpnpm check:gitlink-declaredpnpm check:issue-citationspnpm check:nul-bytespnpm check:parse-guardpnpm check:partof-closing-keywordpnpm check:pm-governed-mergespnpm check:pm-half-statespnpm check:pm-post-stampedpnpm check:pm-skill-id-lintpnpm check:pm-skill-ratchetpnpm check:pnpm-filter-targets--filteroccurrence(s) across 41 file(s) resolve against 81 workspace package(s); 55 not judged (2 foreign, 30pnpm check:ratchet-remedy-authoritypnpm check:refd-timer-probepnpm check:single-claim-pathspnpm check:skill-frame-syncpnpm check:watch-hint-literalpnpm check:pm-dispatch-gatesRow 20 is exit 3 = PREREQUISITE NOT MET (the gate's own NOT-MEASURED code, see Acceptance notes); every other row exit 0. The same 47 ran at
bd5bbd2bbwith the same readings before the merge oforigin/main.Line budget
.claude/agents/os-dev.md: 402 / ceiling 402 before and after (headroom 0, net 0 lines);check:pm-skill-ratchetexit 0 on the head. Max content bytes per line 120 before and after.skills/**(the published catalog) is not touched, so no whole-file / whole-package token readings are owed..claude/**andscripts/pm/**publish nothing from any package'sfiles[](fast lane), soskip-changesetapplies and no changeset is written.On the card
landing-operations.md:27–:28 and the queue guard's SUCCESS line: landed by PR fix(pm): repair two carriers still spelling the superseded references-tier boundary #19379 (merged1f53b0b685); the guard's :236 hit is a verbatim ruling quotation and stays.os-dev.md:286–:287, H48, H43: this PR.origin/mainatfae870352and not in this PR's claimed file surface:.claude/skills/pm-dispatch/SKILL.md:618(the size clause ending 「⛔ 无事实层例外」) still spells the retired word (the skills seat's addendum 5737973707 joined it to this card), and 「规则层」 survives as a synonym for Tier H atSKILL.md:617,references/core-rules.md:122,references/landing-operations.md:26andreferences/lanes/skills.md:17(vocabulary only; each rule stays true). That is why the first line isPart ofrather than a closing keyword: skills: re-key the three 事实层 = references/ spellings the Tier S ruling leaves false (os-dev.md :286–:287, landing-operations.md :27–:28, H48) #19146 remains open after this PR merges, with its own addendum item still owed. The dispatch asked for a closing first line on the premise that the twoos-dev.mdlines were the whole remainder; the tree-wide grep says otherwise, and os-dev.md's rule (a PR whose merge should not close the card usesPart of) wins.Acceptance notes (observations, not filed)
check-half-states.mjsH48's sentence still says the handoff exists because "a Tier H surface lands only on the maintainer's word" — accurate for Tier H under PD feat: Comprehensive CRM example demonstrating all ObjectStack protocol features #14 (the maintainer's hand or an authorized approval); the pre-existing "by hand" wording was narrowed to that in the same edit.pnpm --filter @objectstack/lint run check:doc-formula-expressionsanswers exit 3 PREREQUISITE NOT MET in this worktree (the gate needs@objectstack/formula/@objectstack/lintbuilt; this diff touches no package, so no build closure is owed). Recorded as NOT MEASURED, not as a failure; its population (docs formula expressions) is disjoint from both changed paths.## Contract reviewrecord for headc9617addeand lands it through the queue after every check is green.Generated by Claude Code